/** Fichier implémentant un dodécaèdre.
 *
 * Ce fichier contient l'implémentation de la classe Dodecaedre.
 *
 * @file Dodecaedre.cpp
 * @author Johan "Solo" Luisier
 * @date 2010
 */
#include "Dodecaedre.hpp"

#include <cmath>

#include "Algebre/ConstruitMatrice.hpp"

#include "Utils/StatusCode.hpp"
#include "Utils/debugstreams.hpp"

#include <algorithm>

#include <boost/foreach.hpp>

using namespace std;

const double Dodecaedre::Phi( ( 1. + sqrt( 5. ) ) / 2. );

/** Constructeur standard.
 *
 * Ce constructeur initialise les 12 faces d'un Dodecaedre. Le principe est
 * d'utiliser les 12 sommets d'un icosaèdre.
 *
 *Ici on itinialise la transformation qui permet de passer des coordonnèes
 *locales (utilisées pour définir les sommets) et les coordonéées globales,
 *utilisées pour les Plans.
 *
 * @throw Erreur si les vecteurs ne sont pas othogonaux.
 *
 * @param[in] centre centre de gravité de du Dodecaedre.
 * @param[in] axeX direction de 0x' dans le référentiel global.
 * @param[in] axeY direction de 0y' dans le référentiel global.
 * @param[in] ratio rapport d'homothétie. Pour un ratio de 1., le
 * Dodecaedre peut être inscrit dans un cube de côté 1.
 */
Dodecaedre::Dodecaedre(const Coord& centre, const VecteurU& axeX,
                       const VecteurU& axeY, const double& ratio)
    : Centre( centre )
{
    MatriceSO3 test;

    StatusCode sc( axesXY( axeX, axeY, test ) );

    if ( ! sc )
    {
        error() << sc.message() << endreq;
        exit( 1 );
    }
    else
    {
        VersGlobal = Transformation( test, Centre, ratio / ( 2. * Phi ), false );
        VersLocal  = VersGlobal.inverse();
    }
    /*
     * On définit premièrement tous les points dans le repère local.
     */

    Coord xy( Phi, 1., 0. );
    Coord xz( 1., 0., Phi );
    Coord yz( 0., Phi, 1. );

    vector< Coord > sommetsLoc, sommetsGlob;
    unsigned int i, j(0);

    for (i = 1; i < 3; i++, j++)
        sommetsLoc.push_back( symetries( xy, i ) );
    for (i = 5; i < 7; i++, j++)
        sommetsLoc.push_back( symetries( xy, i ) );

    for (i = 2; i < 6; i++, j++)
        sommetsLoc.push_back( symetries( xz, i ) );

    for (i = 0; i < 4; i++, j++)
        sommetsLoc.push_back( symetries( yz, i ) );

    sommetsGlob.resize( sommetsLoc.size() );
    transform( sommetsLoc.begin(), sommetsLoc.end(), sommetsGlob.begin(),
               VersGlobal );
    
    BOOST_FOREACH( Coord coord, sommetsGlob)
            listplan.push_back( Plan::SharedPtr( new Plan( Vecteur( Centre, coord ),
                                                           coord ) ) );

    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[0].get(),
                                                                   listplan[1].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[2].get(),
                                                                   listplan[3].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[4].get(),
                                                                   listplan[5].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[6].get(),
                                                                   listplan[7].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[8].get(),
                                                                   listplan[9].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[10].get(),
                                                                   listplan[11].get() ) ) );

    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[0].get(),
                                                                   listFC[1].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[2].get(),
                                                                   listFC[3].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[4].get(),
                                                                   listFC[5].get() ) ) );

    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[6].get(),
                                                                   listFC[7].get() ) ) );

    form[0] = listFC[8].get();
    form[1] = listFC[9].get();

    op = inter;

    Nom = "Dodécaèdre";

    PseudoCentre = Centre;
    PseudoRayon = ratio;
}

/** Destructeur, virtuel pour assurer une séquence de destruction correcte.
 *
 * Le destructeur n'a rien de spécial à faire.
 */
Dodecaedre::~Dodecaedre()
{}

/** Permet d'utiliser les symétries du solide pour générer la liste des
 * sommets.
 *
 * Les sommets de l'icosaèdre répondent à une liste d'opérations de symétrie,
 * qui sont utilisées ici pour générer l'ensemble des sommets à partir des 3
 * sommets de départ.
 *
 * @param[in] pt point correspondant à l'identité.
 * @param[in] i "numéro" de la symétrie à appliquer : 1 signifie que l'on
 * inverse la coordonnée z, 2 la coordonnée y. En fait le nombre, vu
 * en binaire, dit quels sont les inversions effectuées.
 *
 * @return l'image de pt une fois la symétrie voulue appliquée.
 */
Coord Dodecaedre::symetries(const Coord& pt, const unsigned int& i) const
{
    Coord tmp( pt );

    if ( i % 2 == 1 && abs( tmp.z() ) > 1.e-7 )
        tmp.setZ( - tmp.z() );

    if ( ( i % 4 == 2 || i % 4 == 3 ) && abs( tmp.y() ) > 1.e-7 )
        tmp.setY( - tmp.y() );

    if ( i / 4 == 1 && abs( tmp.x() ) > 1.e-7 )
        tmp.setX( - tmp.x() );

    return tmp;
}
